#include "WWebsocketServer.h"

WWebsocketServer::WWebsocketServer(mg_mgr *mgr)
{
    _mgr = mgr;
    if (!_mgr)
    {
        _mgr = new mg_mgr();
        mg_mgr_init(_mgr);
        _selfMgrFlag = true;
    }
}

WWebsocketServer::~WWebsocketServer()
{
    stop();
    if (_selfMgrFlag)
    {
        mg_mgr_free(_mgr);
        delete _mgr;
        _mgr = nullptr;
    }
}

bool WWebsocketServer::startWsServer(int port)
{
    if (_wsPort != -1)
    {
        Logw("WWebsocketServer::startWsServer ws server is already start port:%d", _wsPort);
        return false;
    }
    std::stringstream sstream;
    sstream  << "ws://0.0.0.0:" << port;
    _wsCbMsg.wsServer = this;
    _wsCbMsg.sslFlag = false;
    _wsServerConn = mg_http_listen(_mgr, sstream.str().c_str(), WWebsocketServer::recvWsRequestMsgCb, (void *)&_wsCbMsg);
    if (!_wsServerConn)
    {
        Logw("WWebsocketServer::startWsServer ws server start failed: %s", sstream.str().c_str());
        return false;
    }
    Logi("WWebsocketServer::startWsServer ws server start success: %s", sstream.str().c_str());
    _wsPort = port;
    return true;
}

bool WWebsocketServer::startWssServer(int port, string certPath, string keyPath)
{
    if (_wssPort != -1)
    {
        Logw("WWebsocketServer::startWsServer wss server is already start port:%d", _wsPort);
        return false;
    }
    std::stringstream sstream;
    sstream  << "wss://0.0.0.0:" << port;
    _wssCbMsg.wsServer = this;
    _wssCbMsg.sslFlag = true;
    _wssServerConn = mg_http_listen(_mgr, sstream.str().c_str(), WWebsocketServer::recvWsRequestMsgCb, (void *)&_wssCbMsg);
    if (!_wssServerConn)
    {
        Logw("WWebsocketServer::startWebsocketSslServer wss server start failed: %s", sstream.str().c_str());
        return false;
    }
    Logi("WWebsocketServer::startWebsocketSslServer wss server start success: %s", sstream.str().c_str());
    _certPath = certPath;
    _keyPath = keyPath;
    _wssPort = port;
    return true;
}

void WWebsocketServer::setListener(IWebsocketServerCb *listener)
{
    _listener = listener;
}

bool WWebsocketServer::run()
{
    mg_mgr_poll(_mgr, 1);
    return true;
}

bool WWebsocketServer::stop()
{
    if (_wsPort == -1 && _wssPort == -1) {
        return true;
    }

    _wssPort = -1;
    _wsPort = -1;
    _certPath.clear();
    _keyPath.clear();

    if (_wsServerConn)
    {
        _wsServerConn->is_closing = 1;
    }

    if (_wssServerConn)
    {
        _wssServerConn->is_closing = 1;
    }

    return true;
}

bool WWebsocketServer::sendMsg(mg_connection *wsConnection, const string &msg)
{
    if (_wssPort == -1 && _wsPort == -1)
    {
        return false;
    }

    if (msg.empty())
    {
        return false;
    }

    if (wsConnection->label[W_VALID_CONNECT_BIT] != 1)
    {
        Logw("WWebsocketServer::sendMsg the connection may be disconnected")
        return false;
    }

    mg_ws_send(wsConnection, msg.c_str(), msg.size(), WEBSOCKET_OP_TEXT);
    return true;
}

bool WWebsocketServer::disconnect(mg_connection *wsConnection)
{
    if(wsConnection->label[W_VALID_CONNECT_BIT] != 1)
    {
        Logw("WWebsocketServer::disconnect the connection is invalid");
        return false;
    }

    wsConnection->is_closing = 1;
    return true;
}

void WWebsocketServer::recvWsRequestMsg(mg_connection *conn, int msgType, void *msgData, void *cbData)
{
    WWebsocketCbMsg *cbMsg = (WWebsocketCbMsg *)cbData;
    if (msgType == MG_EV_ACCEPT && cbMsg->sslFlag)
    {
        struct mg_tls_opts opts;
        opts.ca = nullptr;
        opts.cert = _certPath.c_str();
        opts.certkey = _keyPath.c_str();
        opts.ciphers = nullptr;
        opts.srvname.ptr = nullptr;
        opts.srvname.len = 0;
        Logi("WWebsocketServer::recvWsRequestMsg wss connect come id:%ld", conn->id);
        mg_tls_init(conn, &opts);
    }
    else if (msgType == MG_EV_HTTP_MSG) {
        struct mg_http_message *hm = (struct mg_http_message *) msgData;
        logHttpRequestMsg(conn, hm);
        mg_str *upgrade = mg_http_get_header(hm, "Upgrade");
        if (upgrade && (upgrade->len == strlen("websocket")) && (mg_ncasecmp(upgrade->ptr, "websocket", upgrade->len) == 0)) {
            bool isValidWsConnect = true;
            if (_listener)
            {
                isValidWsConnect = _listener->checkIfValidWs(conn, hm);
            }

            if (isValidWsConnect)
            {
                Logi("WWebsocketServer::recvWsRequestMsg will upgrade to websocket id: %ld", conn->id);
                conn->label[W_VALID_CONNECT_BIT] = 1;
                mg_ws_upgrade(conn, hm, NULL);
                if (_listener)
                {
                    _listener->onWsServerConnect(conn); // MG_EV_WS_OPEN只有ws客户端才会回调，所以这个回调只能在这里
                }
            }
            else
            {
                Logw("WWebsocketServer::recvWsRequestMsg is not valid ws client id: %ld", conn->id);
                conn->is_draining = 1;
            }
        }
        else
        {
            Logw("WWebsocketServer::recvWsRequestMsg can not upgrade to websocket id: %ld", conn->id);
            conn->is_draining = 1;
        }
    }
    else if (msgType == MG_EV_WS_MSG)
    {
        struct mg_ws_message *wm = (struct mg_ws_message *) msgData;
        if (mg_ncasecmp(wm->data.ptr, "ping", 4) == 0) // 有的客户端是用text的方式发送ping
        {
            sendMsg(conn, "PONG");
        }
        else
        {
            if (_listener)
            {
                shared_ptr<string> receiveMsg = shared_ptr<string>(new string());
                receiveMsg->resize(wm->data.len);
                memcpy((char *)receiveMsg->c_str(), wm->data.ptr, wm->data.len);
                _listener->onWsServerReceiveMsg(conn, receiveMsg);
            }
        }
        // mg_iobuf_delete(&conn->recv, conn->recv.len); // moogonse内部有调用这个
    }
    else if (msgType == MG_EV_WS_CTL)
    {
        struct mg_ws_message *wm = (struct mg_ws_message *) msgData;
        Logi("WWebsocketServer::recvWsRequestMsg recv ws controll flag: %d, msg:%s", (int)wm->flags, wm->data.ptr);
    }
    else if (msgType == MG_EV_CLOSE)
    {
        if (conn->label[W_VALID_CONNECT_BIT] != 1)
        {
            return;
        }
        Logi("WWebsocketServer::recvWsRequestMsg close ws: %ld", conn->id);
        if (_listener)
        {
            _listener->onWsServerClosed(conn);
        }
    }
}

void WWebsocketServer::logHttpRequestMsg(mg_connection *conn, mg_http_message *httpCbData)
{
    if (httpCbData->message.len < 1024)
    {
        Logi("WWebsocketServer::logHttpRequestMsg %s request id:%ld, message: %s", conn->is_tls ? "https" : "http", conn->id, httpCbData->message.ptr);
    }
    else
    {
        char msg[1025] = {0};
        memcpy(msg, httpCbData->message.ptr, 1024);
        msg[1024] = '\0';
        Logi("WWebsocketServer::logHttpRequestMsg %s request id:%ld, pre 1024 message: %s", conn->is_tls ? "https" : "http", conn->id, msg);
    }
}

void WWebsocketServer::recvWsRequestMsgCb(mg_connection *conn, int msgType, void *msgData, void *cbData)
{
    WWebsocketCbMsg *cbMsg = (WWebsocketCbMsg *)cbData;
    cbMsg->wsServer->recvWsRequestMsg(conn, msgType, msgData, cbData);
}
